package com.qfxl.common.comm;

import sun.misc.BASE64Encoder;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @Title: SecretKey 密钥
 * @Author cj
 * @Package com.cj.common
 * @Date 2025/5/22 11:20
 * @description: 密钥工具类【公钥、私钥】
 */
public class SecretKey {
    /**
     * 指定加密算法为RSA
     */
    private static final String ALGORITHM = "RSA";
    /**
     * 密钥长度，用来初始化
     * 1024、2048
     */
    private static final int KEYSIZE = 2048;
    public static final String PUB_HEADER = "-----BEGIN PUBLIC KEY-----";
    public static final String PUB_FOOTER = "-----END PUBLIC KEY-----";
    public static final String PRI_HEADER = "-----BEGIN PRIVATE KEY-----";
    public static final String PRI_FOOTER = "-----END PRIVATE KEY-----";

    public static void main(String[] args) throws Exception {
        generateRSAKeyPair();

        PublicKey publicKey = loadPublicKey(Paths.get("PublicKey.key"));
        String publicKeyStr = getPublicKeyStr(publicKey);
        System.out.println(publicKeyStr);
    }

    /**
     * 获取密钥对对象
     * <p>
     * //     * @param algorithm algorithm      运算法则 RSA\
     *
     * @param keySize 密钥长度，用来初始化 1024、2048
     * @return 密钥对对象
     * @throws NoSuchAlgorithmException 当请求特定加密算法但在环境中不可用时，将引发此异常
     */
    public static KeyPair generateRSAKeyPairObject(int keySize) throws NoSuchAlgorithmException {
        //     /** RSA算法要求有一个可信任的随机数源 */
        SecureRandom secureRandom = new SecureRandom();
        /** 为RSA算法创建一个KeyPairGenerator对象 */
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

        /** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
        keyPairGenerator.initialize(keySize, secureRandom);
//        keyPairGenerator.initialize(KEYSIZE);

        /** 生成密匙对 */
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 生成密钥对
     * 相对路径，生成路径。
     */
    public static void generateRSAKeyPair() {
        generateRSAKeyPair(2048, "PrivateKey.key", "PublicKey.key");
    }

    /**
     * 生成密钥对
     *
     * @param keySize        密钥长度 1024 2048  ...
     * @param privateKeyPath 私钥地址【相对、绝对】
     * @param publicKeyPath  公钥地址【相对、绝对】
     */
    public static void generateRSAKeyPair(int keySize, String privateKeyPath, String publicKeyPath) {
        try {
            /** 生成密匙对 */
            KeyPair keyPair = generateRSAKeyPairObject(keySize);
            /** 得到公钥 */
            PublicKey publicKey = keyPair.getPublic();
            /** 保存到文件 公钥 */
            savePublicKey(publicKey, Paths.get(publicKeyPath));
            /** 得到私钥 */
            PrivateKey privateKey = keyPair.getPrivate();
            /** 保存到文件 私钥 */
            savePrivateKey(privateKey, Paths.get(privateKeyPath));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将私钥以 PEM 格式保存到文件
     */
    public static void savePrivateKey(PrivateKey pri, Path path) throws IOException {
        byte[] enc = pri.getEncoded();
        String b64 = Base64.getMimeEncoder(64, "\n".getBytes())
                .encodeToString(enc);
        String pem = PRI_HEADER + "\n" + b64 + "\n" + PRI_FOOTER;
        Files.write(path, pem.getBytes(StandardCharsets.US_ASCII));
    }

    /**
     * 将公钥以 PEM 格式保存到文件
     */
    public static void savePublicKey(PublicKey pub, Path path) throws IOException {
        byte[] enc = pub.getEncoded();
        String b64 = Base64.getMimeEncoder(64, "\n".getBytes())
                .encodeToString(enc);
        String pem = PUB_HEADER + "\n" + b64 + "\n" + PUB_FOOTER;
        Files.write(path, pem.getBytes(StandardCharsets.US_ASCII));
    }

    /**
     * 从 PEM 文件加载公钥
     */
    public static PublicKey loadPublicKey(Path path) throws Exception {
        String pem = new String(Files.readAllBytes(path), StandardCharsets.US_ASCII);
        String b64 = pem
                .replace(PUB_HEADER, "")
                .replace(PUB_FOOTER, "")
                .replaceAll("\\s+", "");
        byte[] decoded = Base64.getDecoder().decode(b64);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
    }

    /**
     * 从 PEM 文件加载私钥
     */
    public static PrivateKey loadPrivateKey(Path path) throws Exception {
        String pem = new String(Files.readAllBytes(path), StandardCharsets.US_ASCII);
        String b64 = pem
                .replace(PRI_HEADER, "")
                .replace(PRI_FOOTER, "")
                .replaceAll("\\s+", "");
        byte[] decoded = Base64.getDecoder().decode(b64);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }

    /**
     * 获取公钥字符串
     *
     * @param publicKey 公钥
     * @return 私钥字符串
     */
    public static String getPublicKeyStr(PublicKey publicKey) {
        byte[] publicKeyBytes = publicKey.getEncoded();
        String publicKeyBase64 = new BASE64Encoder().encode(publicKeyBytes);
        return publicKeyBase64;
    }


    /**
     * 获取私钥对象
     *
     * @param privateKeyPath 私钥文件地址
     * @param algorithm      运算规则 RSA
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKeyObject(String privateKeyPath, String algorithm) {
        try {
            InputStream resourceAsStream = Files.newInputStream(Paths.get(privateKeyPath));
            DataInputStream dis = new DataInputStream(resourceAsStream);
            byte[] keyBytes = new byte[resourceAsStream.available()];
            dis.readFully(keyBytes);
            dis.close();
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory kf = KeyFactory.getInstance(algorithm);
            return kf.generatePrivate(spec);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取私钥字符串
     *
     * @param privateKey 私钥
     * @return 私钥字符串
     */
    public static String getPrivateKeyStr(PrivateKey privateKey) {
        byte[] privateKeyBytes = privateKey.getEncoded();
        String privateKeyBase64 = new BASE64Encoder().encode(privateKeyBytes);
        return privateKeyBase64;
    }


    private static void genKeyPair() throws NoSuchAlgorithmException {

        /** RSA算法要求有一个可信任的随机数源 */
        SecureRandom secureRandom = new SecureRandom();

        /** 为RSA算法创建一个KeyPairGenerator对象 */
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);

        /** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
        keyPairGenerator.initialize(KEYSIZE, secureRandom);
        //keyPairGenerator.initialize(KEYSIZE);

        /** 生成密匙对 */
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        /** 得到公钥 */
        Key publicKey = keyPair.getPublic();

        /** 得到私钥 */
        Key privateKey = keyPair.getPrivate();

        byte[] publicKeyBytes = publicKey.getEncoded();
        byte[] privateKeyBytes = privateKey.getEncoded();

        String publicKeyBase64 = new BASE64Encoder().encode(publicKeyBytes);
        String privateKeyBase64 = new BASE64Encoder().encode(privateKeyBytes);

        System.out.println("publicKeyBase64.length():" + publicKeyBase64.length());
        System.out.println("publicKeyBase64:" + publicKeyBase64);

        System.out.println("privateKeyBase64.length():" + privateKeyBase64.length());
        System.out.println("privateKeyBase64:" + privateKeyBase64);
    }
}
